﻿using System;
using System.Linq;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.Framework.Server;
using Microsoft.TeamFoundation.VersionControl.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using CodePlex.TfsPluginSuite.Configuration;
using CodePlex.TfsPluginSuite.Helper;
using Microsoft.TeamFoundation.Server;
using System.Text.RegularExpressions;

namespace CodePlex.TfsPluginSuite
{
    public class WorkItemAssociationPolicyPlugin : ISubscriber
    {
        public string Name
        {
            get { return "CodePlex.TfsPluginSuite.WorkItemAssociationPolicy"; }
        }

        public SubscriberPriority Priority
        {
            get { return SubscriberPriority.Normal; }
        }

        public Type[] SubscribedTypes()
        {
            return new Type[1] { typeof(CheckinNotification) };
        }

        public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext,
            NotificationType notificationType,
            object notificationEventArgs,
            out int statusCode,
            out string statusMessage,
            out ExceptionPropertyCollection properties)
        {
            EventNotificationStatus response = EventNotificationStatus.ActionPermitted;
            properties = new ExceptionPropertyCollection();
            statusMessage = String.Empty;
            statusCode = 0;

            var config = TfsPluginSuiteConfigurationManager.Section.WorkItemAssociationPolicy;

            if (!config.Enabled)
            {
                return response;
            }

            // If this is a check-in notification and is a decision point
            if (notificationEventArgs is CheckinNotification && notificationType == NotificationType.DecisionPoint)
            {
                var args = notificationEventArgs as CheckinNotification;
                var tfsHelper = new TfsHelper(requestContext);
                var workItemStore = tfsHelper.GetWorkItemStore();
                var associatedWorkItems = args.NotificationInfo.WorkItemInfo;
              
                //This policy is only useful if we have associated work items
                if (associatedWorkItems.Count() == 0)
                {
                    statusMessage = "You must associate this check-in with a work item.";
                    return EventNotificationStatus.ActionDenied;
                }

                //Denied work item policy
                if (config.DeniedWorkItemsPolicy.Enabled)
                {
                    foreach (DenyWorkItemsConfigurationElement denyWorkItems in config.DeniedWorkItemsPolicy)
                    {
                        string resolvedWiq = ResolveCustomWiqMacros(denyWorkItems.WorkItemQuery, associatedWorkItems);

                        Query query = new Query(workItemStore, resolvedWiq);

                        //running the count query only to support link queries and for performance
                        if (query.RunCountQuery() > 0)
                        {
                            statusMessage = denyWorkItems.Message;
                            return EventNotificationStatus.ActionDenied;
                        }
                    }
                }

                //Required work item policy
                if (config.RequiredWorkItemsPolicy.Enabled)
                {
                    foreach (RequiredWorkItemsConfigurationElement requiredWorkItems in config.RequiredWorkItemsPolicy)
                    {
                        string resolvedWiq = ResolveCustomWiqMacros(requiredWorkItems.WorkItemQuery, associatedWorkItems);

                        Query query = new Query(workItemStore, resolvedWiq);
                        int queryCount = query.RunCountQuery();
                        if (queryCount == 0 || queryCount > requiredWorkItems.WorkInProgressLimit)
                        {
                            statusMessage = requiredWorkItems.Message;
                            return EventNotificationStatus.ActionDenied;
                        }

                        if (requiredWorkItems.CheckIterationDates)
                        {
                            foreach (var workItemInfo in associatedWorkItems)
                            {
                                NodeInfo iterationNodeInfo = tfsHelper.GetIterationInfo(workItemInfo.Id);
                                var iterationStartDate = iterationNodeInfo.StartDate;
                                var iterationFinishDate = iterationNodeInfo.FinishDate;

                                if (iterationStartDate == null || iterationFinishDate == null)
                                {
                                    statusMessage = "Associated work items have an iteration with no start and finish dates.";
                                    response = EventNotificationStatus.ActionDenied;
                                }
                                else if (DateTime.Now < iterationStartDate)
                                {
                                    statusMessage = "Associated work items have a future iteration that hasn't started yet.";
                                    response = EventNotificationStatus.ActionDenied;
                                }
                                else if (DateTime.Now > iterationFinishDate)
                                {
                                    statusMessage = "Associated work items have a past iteration that has already finished.";
                                    response = EventNotificationStatus.ActionDenied;
                                }
                            }
                        }
                    }
                }
              }

            return response;
        }

        private string ResolveCustomWiqMacros(string wiq, CheckinNotificationWorkItemInfo[] associatedWorkItems)
        {
            string resolvedWiq = null;

            if(Regex.IsMatch(wiq,"@AssociatedWorkItems", RegexOptions.IgnoreCase))
            {
                var workItemIds = associatedWorkItems.AsEnumerable().Select(wiInfo => wiInfo.Id);
                var formattedWorkItemIds = string.Format("({0})", String.Join(",", workItemIds));
                resolvedWiq = Regex.Replace(wiq, "@AssociatedWorkItems", formattedWorkItemIds, RegexOptions.IgnoreCase);
            }    

            return resolvedWiq;
        }
    }
}

